/*
 * Copyright 2011 Angel Sanadinov
 *
 * This file is part of VBox WMI.
 *
 * VBox WMI is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * VBox WMI is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with VBox WMI.  If not, see <http://www.gnu.org/licenses/>.
 */

package ApplicationManagement;

import Managers.LogsManager;
import Utilities.Constants;
import Utilities.Constants.FilterCompressionAlgorithm;
import Utilities.GzipCompressionWrapper;
import Utilities.Interfaces.CompressionResponseWrapper;
import Utilities.ParameterNames;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * This class represents a HTTP response compression filter. <br><br>
 *
 * All requests pass through this filter and their subsequent responses are
 * either compressed or left as is (based on client and server compressions support).
 *
 * <b>Note:</b> <i>Currently, only GZIP compression is supported.</i>
 *
 * @see GzipCompressionWrapper
 *
 * @author Angel Sanadinov
 */
public class CompressionFilter implements Filter
{
    private ServletContext context; //filter context
    private LogsManager logsManager;//logs manager
    private FilterCompressionAlgorithm compressionAlgorithm;//the compression algorithm to be used

    /**
     * Initializes the filter.
     *
     * @param filterConfig filter configuration
     *
     * @throws ServletException if an exception occurs that interrupts the filter's
     *                          normal operation
     *
     * @see Filter#init()
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException
    {
        context = filterConfig.getServletContext();
        compressionAlgorithm = FilterCompressionAlgorithm.NONE;
    }

    /**
     * Ends the filter's life-cycle and cleans up any object references.
     *
     * @see Filter#destroy()
     */
    @Override
    public void destroy()
    {
        context = null;
        logsManager = null;
        compressionAlgorithm = null;
    }
    
    /**
     * Compresses each client response, if possible.
     *
     * @param request The servlet request being processed
     * @param response The servlet response being processed
     * @param chain The filter chain being processed
     *
     * @exception IOException if an input/output error occurs
     * @exception ServletException if a servlet error occurs
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
	throws IOException, ServletException
    {
        //retrieves the logs manager, if it is not available
        if(logsManager == null)
            logsManager = (LogsManager)context.getAttribute(Constants.CONTEXT_LOGS_MANAGER);
        else
            ;

        //attempts to retrieve the compression algorithm type specified in the configuration file
        try
        {
            compressionAlgorithm = FilterCompressionAlgorithm.valueOf(
                    context.getInitParameter(ParameterNames.COMPRESSION_ALGORITHM).toUpperCase());
        }
        catch(Exception e)
        {
            logsManager.logSystemEvent(System.currentTimeMillis(), Constants.LOG_SEVERITY_WARNING,
                                       "Compression Filter: Failed to parse initialization parameter. "
                                       + "Default value (" + compressionAlgorithm
                                       + ") for compressionAlgorithm will be used.");
        }

        //checks if an HTTP request was made and if the logs manager was retrieved
        if(request instanceof HttpServletRequest && logsManager != null)
        {
            String encodingHeader = ((HttpServletRequest)request).getHeader("Accept-Encoding");
            if(encodingHeader != null)
            {
                //attemtps to wrap the response object (based on the server config)
                CompressionResponseWrapper compressedResponse = null;
                switch(compressionAlgorithm)
                {
                    case GZIP:
                    {
                        //checks if the client supports gzip encoding
                        if(encodingHeader.contains("gzip"))
                            compressedResponse = new GzipCompressionWrapper((HttpServletResponse)response);
                        else
                            ;
                    } break;

                    default: ; break;
                }

                //checks if the response object was wrapped
                if(compressedResponse != null)
                {
                    chain.doFilter(request, compressedResponse);
                    compressedResponse.commitResponse();
                    return;
                }
                else //either the application is not confiugred for compression or wrapping failed
                    ;
            }
            else //client does not support encoding
                ;

            //either the client or the server do not support the available encoding
            //algorithms, or the wrapping failed; no compression will be used
            chain.doFilter(request, response);
        }
        else
            throw new ServletException("Failed to retrieve HTTP request object and/or logging manager");
    }

}
